﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel;

namespace Inspire.UIResources
{
    /// <summary>
    /// Interaction logic for AutoComplete.xaml
    /// </summary>
    public partial class AutoComplete : UserControl, INotifyPropertyChanged
    {
        /// <summary>
        /// DependencyProperty for setting the Foreground Color of the textbox.
        /// </summary>
        public static readonly DependencyProperty TextboxForegroundProperty = DependencyProperty.Register("TextboxForeground", typeof(Brush), typeof(AutoComplete), new PropertyMetadata());

        /// <summary>
        /// DependencyProperty for setting the list to search from.
        /// </summary>
        public static readonly DependencyProperty OptionListProperty = DependencyProperty.Register("OptionList", typeof(List<object>), typeof(AutoComplete), new PropertyMetadata(new PropertyChangedCallback(OptionList_PropertyChangedCallback)));

        /// <summary>
        /// DependencyProperty for setting/getting the text in the text box.
        /// </summary>
        public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(String), typeof(AutoComplete), new PropertyMetadata(new PropertyChangedCallback(TextProperty_PropertyChangedCallback)));

        /// <summary>
        /// DependencyProperty for setting/getting the Selected Item.
        /// </summary>
        public static readonly DependencyProperty SelectedItemProperty = DependencyProperty.Register("SelectedItem", typeof(object), typeof(AutoComplete), new PropertyMetadata(new PropertyChangedCallback(SelectedItem_PropertyChangedCallback)));

        /// <summary>
        /// DependencyProperty for setting/getting the ShowOptions Item.
        /// </summary>
        public static readonly DependencyProperty ShowOptionsProperty = DependencyProperty.Register("ShowOptions", typeof(bool), typeof(AutoComplete), new PropertyMetadata());

        /// <summary>
        /// Updates the list shown when the property gets updated.
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="prop"></param>
        public static void OptionList_PropertyChangedCallback(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            AutoComplete currentAutoComplete = (AutoComplete)obj;

            currentAutoComplete.lstSuggestionList.ItemsSource = (List<object>)currentAutoComplete.GetValue(e.Property);
        }

        /// <summary>
        /// Updates the Text property when the SelectedItem changes.
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="e"></param>
        public static void SelectedItem_PropertyChangedCallback(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            AutoComplete currentAutoComplete = (AutoComplete)obj;
            
            if (currentAutoComplete.GetValue(e.Property) != null)
            {
                currentAutoComplete.Text = currentAutoComplete.GetValue(e.Property).ToString();
            }
        }

        /// <summary>
        /// Updates the Search Text when the Text property changes.
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="e"></param>
        public static void TextProperty_PropertyChangedCallback(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            AutoComplete currentAutoComplete = (AutoComplete)obj;

            currentAutoComplete.NotifyPropertyChanged("Text");

            // If the Text changed, clear out the SelectedItem
            if (currentAutoComplete.SelectedItem != null && !(currentAutoComplete.GetValue(e.Property).ToString().Equals(currentAutoComplete.SelectedItem.ToString())))
            {
                currentAutoComplete.SelectedItem = null;
            }
        }

        /**
         * Allow interested parties to sign up to receive the TextChanged and SelectionChanged events.
         */
        public event TextChangedEventHandler TextChanged;
        public event SelectionChangedEventHandler SelectionChanged;

        public AutoComplete()
        {
            SetValue(ShowOptionsProperty, false);
            InitializeComponent();
        }

        /// <summary>
        /// Allows programmatic access to the DependencyProperty that sets the Option List.
        /// </summary>
        public List<object> OptionList
        {
            get
            {
                return (List<object>)GetValue(OptionListProperty);
            }
            set
            {
                SetValue(OptionListProperty, value);
            }
        }

        /// <summary>
        /// Allows programmatic access to the DependencyProperty that sets the Selected Item.
        /// </summary>
        public object SelectedItem
        {
            get
            {
                return GetValue(SelectedItemProperty);
            }
            set
            {
                SetValue(SelectedItemProperty, value);
            }
        }

        /// <summary>
        /// Allows programmatic access to the DependencyProperty that shows/hides the options list.
        /// </summary>
        public bool ShowOptions
        {
            get
            {
                return (bool)GetValue(ShowOptionsProperty);
            }
            set
            {
                SetValue(ShowOptionsProperty, value);
            }
        }

        /// <summary>
        /// Allows programmatic access to the DependencyProperty that sets the textbox's Foreground Color.
        /// </summary>
        public Brush TextboxForeground
        {
            get
            {
                return (Brush)GetValue(TextboxForegroundProperty);
            }
            set
            {
                SetValue(TextboxForegroundProperty, value);
            }
        }

        /// <summary>
        /// Allows programmatic access to the DependencyProperty that sets/gets the textbox's content.
        /// </summary>
        public string Text
        {
            get
            {
                return (string)GetValue(TextProperty);
            }
            set
            {
                SetValue(TextProperty, value);
            }
        }

        /// <summary>
        ///  Forward the SelectionChanged event to interested parties.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void lstSuggestionList_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (this.SelectionChanged != null && this.popSuggestionList.IsOpen == false)
            {
                this.SelectionChanged(sender, e);
            }
        }

        /// <summary>
        /// Forward the TextChanged event to interested parties.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void txtSearch_TextChanged(object sender, TextChangedEventArgs e)
        {
            if (this.TextChanged != null)
            {
                this.TextChanged(this, e);
            }
        }

        /// <summary>
        /// Handle important keys so the user can interact with the drop-down list.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void txtSearch_PreviewKeyDown(object sender, KeyEventArgs e)
        {
            switch (e.Key)
            {
                case Key.Up:
                    {
                        if (this.popSuggestionList.IsOpen)
                        {
                            if (this.lstSuggestionList.SelectedIndex > 0)
                            {
                                this.lstSuggestionList.SelectedIndex = this.lstSuggestionList.SelectedIndex - 1;
                                this.lstSuggestionList.ScrollIntoView(this.lstSuggestionList.SelectedItem);
                                this.lstSuggestionList.Focus();
                            }
                        }
                        break;
                    }

                case Key.Down:
                    {
                        if (!(this.popSuggestionList.IsOpen))
                        {
                            // Clear the SelectedItem because it may change
                            this.SelectedItem = null;

                            this.popSuggestionList.IsOpen = true;
                            this.lstSuggestionList.SelectedIndex = 0;
                            this.lstSuggestionList.Focus();
                        }
                        else
                        {
                            if (this.lstSuggestionList.SelectedIndex < (this.lstSuggestionList.Items.Count - 1))
                            {
                                this.lstSuggestionList.SelectedIndex = this.lstSuggestionList.SelectedIndex + 1;
                                this.lstSuggestionList.ScrollIntoView(this.lstSuggestionList.SelectedItem);
                                this.lstSuggestionList.Focus();
                            }
                        }
                        break;
                    }

                case Key.Enter:
                    {
                        if (this.popSuggestionList.IsOpen)
                        {
                            if (this.OptionList.Count > 0 && this.lstSuggestionList.SelectedItems.Count > 0)
                            {
                                this.SelectedItem = this.lstSuggestionList.SelectedItem;
                            }

                            // Clear the selected item of the list
                            this.lstSuggestionList.SelectedItem = null;
                            this.popSuggestionList.IsOpen = false;
                        }
                        break;
                    }

                case Key.Escape:
                    {
                        // Clear the selected item of the list
                        this.lstSuggestionList.SelectedItem = null;
                        this.popSuggestionList.IsOpen = false;
                        break;
                    }
            }
        }

        /// <summary>
        /// Handle what happens when the user selects an item.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void option_Click(object sender, RoutedEventArgs e)
        {
            this.lstSuggestionList.SelectedItem = ((Button)sender).Content;
            this.SelectedItem = this.lstSuggestionList.SelectedItem;

            // Clear the selected item of the list
            this.lstSuggestionList.SelectedItem = null;
        }

        /// <summary>
        /// When the User Control (a wrapper) gains focus, pass that focus on to the textbox so the user can begin typing.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void UserControl_GotFocus(object sender, RoutedEventArgs e)
        {
            this.txtSearch.Focus();
        }

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;
        private void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        #endregion
    }
}
